package project3;

public class RelayCell extends Cell{
	private int streamId;
	private static final int RECOGNIZED = 0x0000;
	private static final int DIGEST = 0x0000;
	private int bodyLength;
	private byte relayCmdType;
	private byte[] body;
	
	private static final int STREAM_ID_OFFSET = 3;
	private static final int BODY_LENGTH_OFFSET = 11;
	private static final int RELAY_CMDTYPE_OFFSET = 13;
	
	public static final int BODY_OFFSET = 14;
	
	public RelayCell(int circId, int streamId, int bodyLength, byte relayCmdType, byte[] body) {
		super(circId, Util.CellType.RELAY);
		this.streamId = streamId;
		
		if (body == null) {
			throw new IllegalArgumentException("body is null");
		}
		
		// check for body length
		int maxLength = Util.CELL_LENGTH - BODY_OFFSET + 1;
		
		if (bodyLength > maxLength) {
			throw new IllegalArgumentException("bodyLength too long");
		}
		
		if (body.length > maxLength) {
			throw new IllegalArgumentException("body too long");
		}
		
		if (bodyLength != body.length) {
			throw new IllegalArgumentException("body length does not match the length of the body");
		}
		
		this.bodyLength = bodyLength;
		this.relayCmdType = relayCmdType;		
		this.body = body;
	}
	
	// construct a new cell from a 512-byte long byte array (including the header)
	public RelayCell (byte[] byteArray) {
		// create a cell (with circuit id -1)
		super(-1, Util.CellType.RELAY);		
		// reset the circuit id
		this.circId = Util.getIntFromTwoBytes(byteArray, 0);
		
		this.streamId = Util.getIntFromTwoBytes(byteArray, STREAM_ID_OFFSET);
		this.bodyLength = Util.getIntFromTwoBytes(byteArray, BODY_LENGTH_OFFSET);
		this.relayCmdType = byteArray[RELAY_CMDTYPE_OFFSET];
		
		if (bodyLength <= 0)
			this.body = new byte[0];
		else {
			this.body = new byte[bodyLength];
			for (int i = 0; i < this.bodyLength; i++) {
				if ((BODY_OFFSET + i) > byteArray.length) break;
				
				this.body[i] = byteArray[BODY_OFFSET + i];
			}
		}
	}
	
	public byte[] generateByteData() {
		byte[] ret = Util.generateHeader(this.circId, this.cellType, Util.CELL_LENGTH);
		
		ret[STREAM_ID_OFFSET] = (byte) ((streamId >> 8) & 0xFF);
		ret[STREAM_ID_OFFSET + 1] = (byte) (streamId & 0xFF);
		
		ret[BODY_LENGTH_OFFSET] = (byte) ((bodyLength >> 8) & 0xff);
		ret[BODY_LENGTH_OFFSET + 1] = (byte) (bodyLength & 0xff);		
		
		ret[RELAY_CMDTYPE_OFFSET] = relayCmdType;
		
		for (int i = 0; i < bodyLength; i++) {
			if ((BODY_OFFSET + i) >= ret.length) break;
			
			ret[BODY_OFFSET + i] = body[i];
		}
		
		return ret;
	}
	
	// returns the stream id of the relay cell
	public int getStreamId() {
		return this.streamId;
	}
	
	// returns the body length of the relay cell
	public int getBodyLength() {
		return this.bodyLength;
	}
	
	// returns the relay command type of the relay cell
	public byte getRelayCmdType() {
		return this.relayCmdType;
	}
	
	// returns the body of the relay cell
	public byte[] getBody() {
		return this.body;
	}

	public void setStreamId(int streamId) {
		this.streamId = streamId;
	}

	public void setBodyLength(int bodyLength) {
		this.bodyLength = bodyLength;
	}

	public void setRelayCmdType(byte relayCmdType) {
		this.relayCmdType = relayCmdType;
	}

	public void setBody(byte[] body) {
		this.body = body;
	}	
	
}
